/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.googlecode.j4sc.submission.manager;

import java.util.Date;
import java.util.HashMap;

import com.googlecode.j4sc.submission.SubmissionReference;

/**
 * This BasicRetrievalStrategy when set as the {@link RetrievalStrategy} for the
 * {@link SubmissionManager} attempts to retrieve a Conversion for a
 * {@link SubmissionReference} every one minute which is the most frequent
 * period recommend for by the SpinVox Create Service.
 * 
 * It will throw an {@link ExpiredSubmissionReferenceException} after a max
 * number of retrieval attempts has been made against a
 * {@link SubmissionReference} upon which the {@link SubmissionManager} will no
 * longer make any further attempts to retrieve the submission.
 * 
 * @author Raj Patel, Dariusz Lorenc
 */
public class BasicRetrievalStrategy implements RetrievalStrategy {

    static final int ONE_MINUTE_POLLING_INTERVAL = 60000;

    final long pollingInterval;
    final HashMap<SubmissionReference, PollingHistory> referencePollingHistory;
    final int maxAttempts;

    class PollingHistory {

        private long time;
        private int attempts;

        public void setTime(long time) {
            this.time = time;
            this.attempts++;
        }

        public long getTime() {
            return time;
        }

        public boolean hasExceededMaxAttempts() {
            return attempts == maxAttempts;
        }

        public int getAttempts() {
            return attempts;
        }
    }

    /**
     * Construct a new instance
     * 
     * @param maxAttempts
     *            the maximum number of retrieval attempts for a
     *            {@link SubmissionReference}, must be at least 1
     */
    public BasicRetrievalStrategy(int maxAttempts) {
        this(ONE_MINUTE_POLLING_INTERVAL, maxAttempts);
    }

    BasicRetrievalStrategy(int pollingInterval, int maxAttempts) {
        if (maxAttempts < 1) {
            throw new IllegalArgumentException("maxAttempts must be at least one");
        }

        this.maxAttempts = maxAttempts;
        this.pollingInterval = pollingInterval;
        this.referencePollingHistory = new HashMap<SubmissionReference, PollingHistory>();
    }

    /*
     * (non-Javadoc)
     * 
     * @seecom.googlecode.j4sc.submission.manager.RetrievalStrategy#
     * isSubmissionDueForRetrieval
     * (com.googlecode.j4sc.submission.SubmissionReference)
     */
    public boolean isDueForRetrieval(SubmissionReference reference) throws ExpiredSubmissionReferenceException {
        return isDueForRetrieval(reference, false);
    }

    boolean isDueForRetrieval(SubmissionReference reference, boolean usingProgressiveMultiplier)
            throws ExpiredSubmissionReferenceException {
        if (referencePollingHistory.containsKey(reference)) {
            PollingHistory pollingHistory = referencePollingHistory.get(reference);
            checkIfExpired(reference, pollingHistory);

            long lastPollTime = pollingHistory.getTime();
            long timeSinceLastPoll = new Date().getTime() - lastPollTime;

            if (timeSinceLastPoll >= pollingInterval
                    * getPollingIntervalMultiplier(usingProgressiveMultiplier, pollingHistory)) {
                pollingHistory.setTime(new Date().getTime());
                return true;
            }

        } else {
            addNew(reference);
        }

        return false;
    }
    
    private int getPollingIntervalMultiplier(boolean usingProgressiveMultiplier, PollingHistory pollingHistory) {
        int multiplier = 1;
        if (usingProgressiveMultiplier) {
            multiplier = pollingHistory.getAttempts();
        }
        return multiplier;
    }

    private void addNew(SubmissionReference reference) {
        PollingHistory lastPoll = new PollingHistory();
        lastPoll.setTime(new Date().getTime());
        referencePollingHistory.put(reference, lastPoll);
    }

    private void checkIfExpired(SubmissionReference reference, PollingHistory pollingHistory)
            throws ExpiredSubmissionReferenceException {
        if (pollingHistory.hasExceededMaxAttempts()) {
            throw new ExpiredSubmissionReferenceException(reference);
        }
    }
}
